import Chromosome from './Chromosome.js';
import evaluateMFEA from './evaluateMFEA.js';
import rand from './rand.js';
import crossover from './crossover.js';
import mutate from './mutate.js';

function MFEA(
  matrixs,
  edgeDatas,
  pop,
  gen,
  selectionProcess,
  rmp,
  mutateProbability,
  useLocalSearch,
  useLocalSearchProbability,
  targetFunction = 'H',
  w
) {
  const matrixNum = matrixs.length;
  if (matrixNum <= 1) throw new Error('At least 2 tasks requires for MFEA');
  if (pop % 2 !== 0) pop = pop + 1;
  let maxNodeNum = 0;
  for (let i = 0; i < matrixNum; i++) {
    if (matrixs[i].length > maxNodeNum) {
      maxNodeNum = matrixs[i].length;
    }
  }
  const result = new Array(gen);
  let bestValues = new Array(matrixNum);
  let population = new Array(pop);
  // 初始化
  for (let i = 0; i < pop; i++) {
    population[i] = new Chromosome(matrixNum, maxNodeNum);
    population[i].initialize(matrixs[rand(0, matrixNum - 1)]);
  }

  // 评估，计算因子代价
  for (let i = 0; i < pop; i++) {
    evaluateMFEA(
      matrixs,
      edgeDatas,
      population[i],
      useLocalSearch,
      useLocalSearchProbability,
      targetFunction,
      bestValues,
      w
    );
  }
  // 计算因子等级
  for (let i = 0; i < matrixNum; i++) {
    population.sort((a, b) => b.factorialCosts[i] - a.factorialCosts[i]);
    for (let j = 0; j < pop; j++) {
      population[j].factorialRanks[i] = j;
    }
    bestValues[i] = {
      rnvec: population[0].rnvec,
      factorialCost: population[0].factorialCosts[i],
    };
  }
  result[0] = [...bestValues];
  // 计算技能因素
  for (let i = 0; i < pop; i++) {
    let minFactorialRank = Math.min(...population[i].factorialRanks);
    let minFactorialRankIndexs = [];
    for (let j = 0; j < matrixNum; j++) {
      if (population[i].factorialRanks[j] === minFactorialRank) {
        minFactorialRankIndexs.push(j);
      }
    }
    population[i].skillFactor = minFactorialRankIndexs[rand(0, minFactorialRankIndexs.length - 1)];
  }
  // 迭代交叉变异
  for (let g = 1; g < gen; g++) {
    const childs = [];
    // 打乱顺序
    population.sort((a, b) => Math.random() - 0.5);
    for (let i = 0; i < pop / 2; i++) {
      let index1 = i;
      let index2 = i + pop / 2;
      if (
        population[index1].skillFactor === population[index2].skillFactor ||
        Math.random() < rmp
      ) {
        let child1 = new Chromosome(matrixNum, maxNodeNum);
        let child2 = new Chromosome(matrixNum, maxNodeNum);
        child1.rnvec = crossover(population[index1].rnvec, population[index2].rnvec);
        child2.rnvec = crossover(population[index2].rnvec, population[index1].rnvec);
        if (Math.random() < 0.5) {
          child1.skillFactor = population[index1].skillFactor;
        } else {
          child1.skillFactor = population[index2].skillFactor;
        }
        if (Math.random() < mutateProbability) {
          mutate(child1.rnvec, matrixs[child1.skillFactor]);
        }
        if (Math.random() < 0.5) {
          child2.skillFactor = population[index1].skillFactor;
        } else {
          child2.skillFactor = population[index2].skillFactor;
        }
        if (Math.random() < mutateProbability) {
          mutate(child2.rnvec, matrixs[child2.skillFactor]);
        }
        childs.push(child1);
        childs.push(child2);
      } else {
        let child1 = new Chromosome(matrixNum, maxNodeNum);
        let child2 = new Chromosome(matrixNum, maxNodeNum);
        child1.rnvec = [...population[index1].rnvec];
        child2.rnvec = [...population[index2].rnvec];
        child1.skillFactor = child2.skillFactor = population[index1].skillFactor;
        mutate(child1.rnvec, matrixs[child1.skillFactor]);
        mutate(child2.rnvec, matrixs[child2.skillFactor]);
        childs.push(child1);
        childs.push(child2);
      }
    }
    for (let i = 0; i < pop; i++) {
      evaluateMFEA(
        matrixs,
        edgeDatas,
        childs[i],
        useLocalSearch,
        useLocalSearchProbability,
        targetFunction,
        bestValues,
        w
      );
    }
    // 合并子代和父代
    let parentsAndChilds = [...population, ...childs];
    // 计算因子等级
    for (let i = 0; i < matrixNum; i++) {
      parentsAndChilds.sort((a, b) => b.factorialCosts[i] - a.factorialCosts[i]);
      for (let j = 0; j < pop * 2; j++) {
        parentsAndChilds[j].factorialRanks[i] = j;
      }
      if (parentsAndChilds[0].factorialCosts[i] > bestValues[i].factorialCost) {
        bestValues[i] = {
          rnvec: parentsAndChilds[0].rnvec,
          factorialCost: parentsAndChilds[0].factorialCosts[i],
        };
      }
    }
    result[g] = [...bestValues];
    console.log(...bestValues.map(item => item.factorialCost));
    // 计算技能因素
    for (let i = 0; i < pop * 2; i++) {
      let minFactorialRank = Math.min(...parentsAndChilds[i].factorialRanks);
      parentsAndChilds[i].scalarFitness = 1 / minFactorialRank;
      let minFactorialRankIndexs = [];
      for (let j = 0; j < matrixNum; j++) {
        if (parentsAndChilds[i].factorialRanks[j] === minFactorialRank) {
          minFactorialRankIndexs.push(j);
        }
      }
      parentsAndChilds[i].skillFactor =
        minFactorialRankIndexs[rand(0, minFactorialRankIndexs.length - 1)];
    }
    // 选择算子
    if (selectionProcess === 'elitist') {
      parentsAndChilds.sort((a, b) => b.scalarFitness - a.scalarFitness);
      population = parentsAndChilds.slice(0, pop);
    }
  }
  const consoleTable = result.map(item => item.map(item1 => item1.factorialCost));
  console.log('MFEA Modularity');
  console.table(consoleTable);
  return result;
}
export default MFEA;
